package com.gitee.wsl.ext.coroutines

import androidx.annotation.RestrictTo
import com.gitee.wsl.api.Closeable
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
inline fun <T> corbindReceiveChannel(
    capacity: Int = Channel.RENDEZVOUS,
    block: Channel<T>.() -> Unit,
): ReceiveChannel<T> = Channel<T>(capacity).apply(block)

fun <T> Channel<T>.split(): Pair<SendChannel<T>, ReceiveChannel<T>> = this to this

fun <T> channels(
    capacity: Int = Channel.RENDEZVOUS,
    onBufferOverlow: BufferOverflow = BufferOverflow.SUSPEND,
): Pair<SendChannel<T>, ReceiveChannel<T>> = Channel<T>(capacity, onBufferOverflow = onBufferOverlow).split()

inline fun <T, R> SendChannel<T>.use(block: (SendChannel<T>) -> R): R {
    var cause: Throwable? = null
    try {
        return block(this)
    } catch (ex: Throwable) {
        cause = ex
        throw ex
    } finally {
        close(cause)
    }
}


private val onUndeliveredCloseable: (Closeable) -> Unit = Closeable::close

fun <E : Closeable> channelForCloseable(capacity: Int): Channel<E> =
    Channel(capacity, onUndeliveredElement = onUndeliveredCloseable)

fun Channel<out Closeable>.cancelWithCause(cause: Throwable?) {
    close(cause) // close channel to provide right cause
    cancel() // force call of onUndeliveredElement to release buffered elements
}
